<!--*************************************
* @update:  2021-06-23  LiLinBin  [创建文件]
* @update:  2021-06-23  LiLinBin  [编写功能]
* @update:  2022-02-18  LiLinBin  [增加接口选择]
* 
* @description: 组件的数据配置页
****************************************-->
 
<template>
    <div class='data-config'>
        <a-collapse v-model:activeKey="activeKey"
            :accordion="accordion">
            <template #expandIcon="{ isActive }">
                <caret-right-outlined :rotate="isActive ? 90 : 0" />
            </template>
            <a-collapse-panel key="1"
                header="数据源">
                <a-form :labelCol="labelCol"
                    :wrapperCol="wrapperCol"
                    :labelAlign="labelAlign">
                    <a-form-item label="数据源类型">
                        <a-select v-model:value="copyDataConfig.source"
                            @change="changeSourceType"
                            placeholder="请选择">
                            <a-select-option value="static">静态数据</a-select-option>
                            <a-select-option value="superSource">superSource接口</a-select-option>
                            <a-select-option value="file">外部文件</a-select-option>
                            <a-select-option value="url">数据接口</a-select-option>
                            <a-select-option value="database">数据库(MySQL)</a-select-option>
                        </a-select>
                    </a-form-item>
                    <a-form-item label="数据源" v-show="copyDataConfig.source!=='static'">
                        <a-row>
                            <a-col :span="21">
                                <a-select
                                    v-model:value="copyDataConfig.templateSourceId"
                                    show-search
                                    placeholder="请选择"
                                    optionFilterProp="label"
                                    @change="changeTemplateSource"
                                    :options="sourceTemplatesByType.map(item => ({ label: item.sourceName, value: item.sourceId }))"
                                ></a-select>
                            </a-col>
                            <a-col :span="3">
                                <a-tooltip placement="top">
                                    <template #title>
                                        <div style="whiteSpace:pre-wrap">
                                            {{currSourceTextInfo}}
                                        </div>
                                    </template>
                                    <div style="text-align:center;">
                                        <question-circle-outlined />
                                    </div>
                                </a-tooltip>
                            </a-col>
                        </a-row>
                    </a-form-item>
                    <a-form-item label="自动更新">
                        <a-switch v-model:checked="copyDataConfig.isAutoUpdate"/>
                    </a-form-item>
                    <a-form-item label="更新频率">
                        <a-row>
                            <a-col :span="21">
                                <a-input v-model:value.trim="copyDataConfig.time"
                                    :disabled="!copyDataConfig.isAutoUpdate"
                                    placeholder="时:分:秒:毫秒"
                                    style="letter-spacing: 4px"/>
                            </a-col>
                            <a-col :span="3">
                                <a-tooltip placement="top">
                                    <template #title>
                                        <div>格式为"时:分:秒:毫秒"</div>
                                    </template>
                                    <div style="text-align:center;">
                                        <question-circle-outlined />
                                    </div>
                                </a-tooltip>
                            </a-col>
                        </a-row>
                    </a-form-item>
                    <div v-show="copyDataConfig.source==='database'">
                        <a-form-item label="sql语句">
                            <a-textarea v-model:value="copyDataConfig.dbSql"
                                :placeholder="`请填写,例如:SELECT * FROM TABLE`"
                                :rows="5" />
                        </a-form-item>
                    </div>
                    <div v-show="['url','superSource'].indexOf(copyDataConfig.source)!==-1">
                        <a-form-item label="接口选择"
                            v-show="copyDataConfig.source==='superSource'">
                            <a-select v-model:value="selectSuperSoure"
                                show-search
                                placeholder="搜索接口"
                                :allowClear="true"
                                :options="superSourceOption"
                                :filter-option="filterOption"
                                @focus="handleFocus"
                                @blur="handleBlur"
                                @change="handleChange"></a-select>
                        </a-form-item>
                        <a-form-item label="接口地址">
                            <a-textarea v-model:value="copyDataConfig.url" 
                                :placeholder="`请填写,例如:\n${copyDataConfig.source==='superSource' ? '?__name__=xxx&__result_format__=%s' : 'http://127.0.0.1:8080/xxxx?params=xxx'}`"
                                :rows="5"/>
                        </a-form-item>
                        <a-form-item label="请求方式">
                            <a-select v-model:value="copyDataConfig.type"
                                placeholder="请选择">
                                <a-select-option value="get">get</a-select-option>
                                <a-select-option value="post">post</a-select-option>
                            </a-select>
                        </a-form-item>
                        <a-form-item label="查询参数">
                            <a-textarea v-model:value="params"
                                placeholder="查询参数"
                                :rows="4" />
                        </a-form-item>
                        <a-form-item label="数据过滤">
                            <a-textarea v-model:value="copyDataConfig.dataFilterJs"
                                placeholder="例如: return resData.data.map(o => o)"
                                :rows="4" />
                        </a-form-item>
                    </div>

                </a-form>

                <!-- 按钮 -->
                <a-row class="mt-20 mb-20" >
                    <a-col :span="8"
                        :offset="16">
                        <a-button type="primary"
                            style="width:100%"
                            @click="saveEdit">数据演示</a-button>
                    </a-col>
                </a-row>

                <!-- 文本框 -->
                <a-textarea v-model:value="staticData"
                    :disabled="copyDataConfig.source!=='static'"
                    placeholder="接口数据"
                    :rows="20" />
            </a-collapse-panel>

            <!-- 配置数据 -->
            <a-collapse-panel key='99'
                header='配置数据'>
                <a-form :labelCol='{span: 0}'
                    :wrapperCol='{span: 24}'
                    :labelAlign='labelAlign'>
                    <a-form-item label=''>
                        <a-textarea v-model:value='dataConfigStr'
                            placeholder=''
                            :rows='30' />
                    </a-form-item>
                </a-form>

                <!-- 按钮 -->
                <a-row class="mt-20 mb-10">
                    <a-col :span="8"
                        :offset="16">
                        <a-button type="primary"
                            @click="updateConfig">更新配置</a-button>
                    </a-col>
                </a-row>
            </a-collapse-panel>
        </a-collapse>
    </div>
</template>

<script>
import { createVNode } from 'vue'
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
import attrConfigMixin from 'tools/mixins/1_attrConfigMixin'

export default {
    des: '组件的数据配置页',
    name: 'dataConfig',
    components: {},
    mixins: [attrConfigMixin],
    props: {
        //接收传递过来的dataConf
        dataConfig: {
            type: Object,
            default: () => ({})
        },
        //接收传递过来的组件ID
        comId: {
            type: String,
            default: () => ('')
        }
    },
    setup(props, { attrs, slots, emit }) { },
    data() {
        return {
            width: '300px',
            activeKey: ["1"],

            myDataConfig: {},       //平台会监听这个数据
            copyDataConfig: {},
            dataConfigStr: '',

            params: "",             //查询参数
            staticData: "",

            selectSuperSoure: "",   //选择superSouce接口的name
            superSourceOption: [],   //所有的superSource接口

            // 数据源模块，新增变量
            sourceTemplates: [], // 数据源合集
            sourceTemplatesByType: [], // 某一类型数据源合集
            currSource: null,
            currSourceTextInfo: '',

            // 已绑定的数据源Id
            originalSourceId: ''
        }
    },
    computed: {},
    watch: {
        //监听平台传递过来的dataConfig
        dataConfig: {
            handler(newO, oldO) {
                this.initData()
            },
            deep: true
        },
        copyDataConfig: {
            handler(newO, oldO) {
                this.dataConfigStr = JSON.stringify(newO, null, 2)
            },
            deep: true
        },
        currSource: {
            handler() {
                const {
                    createDate,
                    createUser,
                    des,
                    sourceDetail
                } = this.currSource
                let str = `
    创建日期：${createDate}
    创建用户：${createUser}
    描述：${des}`
                const sourceDetailObj = JSON.parse(sourceDetail)
                const type = this.currSource.sourceType
                if (type === 'url'){
                    str += `
    根URL：${sourceDetailObj.root}`
                }else if (type === 'superSource'){
                    str += `
    根URL：${sourceDetailObj.root}
    项目：${sourceDetailObj.superSourceProject}`
                } else if (type==='database'){
                    str += `
    IP地址：${sourceDetailObj.database.ip}
    端口号：${sourceDetailObj.database.port}
    用户名：${sourceDetailObj.database.userName}
    数据库名称：${sourceDetailObj.database.dbName}`
                }
                this.currSourceTextInfo = str
            },
            deep: true
        }
    },
    created() {
        this.init()
    },
    mounted() {
        eventBus.on('editor/index.vue:检查更新API配置信息', (params) => {
            if (params.removeIds) {
                const existIds = JSON.parse(params.pageInfo.pageInfo).layerList.map(o => o.id)
                params.removeIds.forEach(removeId=>{
                    if(!existIds.includes(removeId)){
                        // 删除API
                        this.updateApi({params : {id: removeId}}, true)
                    }
                })
            }
            if (!params.comInfo) return
            // 静态数据不存入API，按照旧版本方式存储
            if (this.myDataConfig.source === 'static'){
                // 如果该组件之前是有API, 后来修改为静态数据，删除API
                if(this.dataConfig.templateSourceId){
                    // 删除API
                    this.updateApi({params : {id: params.comInfo.id}}, true)
                }
            } else {
                const args = {
                    id: params.comInfo.id, // 组件ID
                    pageId: params.pageInfo.pageId,
                    name: params.pageInfo.name + ' - ' + params.comInfo.name,
                    sourceId: this.currSource.sourceId,
                    sourceType: this.currSource.sourceType,
                    content: JSON.stringify(this.myDataConfig)
                }
                // 新增或修改API
                this.updateApi(args, false)
            }
        })
        // eventBus.on('editor/index.vue:检查更新API配置信息', (params) => {
        //     // 静态数据不存入API，按照旧版本方式存储
        //     if (this.myDataConfig.source === 'static'){
        //         // 如果该组件之前是有API, 后来修改为静态数据，删除API
        //         if(this.dataConfig.templateSourceId){
        //             // 删除API
        //             this.updateApi({params : {id: params.comInfo.id}}, true)
        //         }
        //     } else {
        //         const args = {
        //             id: params.comInfo.id, // 组件ID
        //             pageId: params.pageInfo.pageId,
        //             name: params.pageInfo.name + ' - ' + params.comInfo.name,
        //             sourceId: this.currSource.sourceId,
        //             sourceType: this.currSource.sourceType,
        //             content: JSON.stringify(this.myDataConfig)
        //         }
        //         // 新增或修改API
        //         this.updateApi(args, false)
        //     }
        // })
    },
    activated() { },
    methods: {
        init() {
            this.getSourceTemplates()
        },

        // 加载当前组件数据配置信息
        initData() {
            if (_.isEqual(this.dataConfig, this.copyDataConfig)) return
            this.myDataConfig = _.cloneDeep(this.dataConfig || {})
            this.copyDataConfig = _.cloneDeep(this.dataConfig || {})
            this.originalSourceId = this.copyDataConfig.templateSourceId
            const initAnyway = ()=>{
                // 显示当前类型的数据源
                this.changeSourceType(this.copyDataConfig.source)

                // 回显格式化
                // this.params = JSON.stringify(this.copyDataConfig.params || {}, null, 4)
                // if (this.copyDataConfig.url) {
                //     const match = this.copyDataConfig.url.match(/__name__=([^\&]+)/) || []
                //     this.selectSuperSoure = match[1] || ""
                // }
                if (this.copyDataConfig.source==='url'){
                    this.params = JSON.stringify(this.copyDataConfig.params || {}, null, 4)
                }else if(this.copyDataConfig.source==='superSource'){
                    this.params = JSON.stringify(this.copyDataConfig.params || {}, null, 4)
                    if (this.copyDataConfig.url) {
                        const match = this.copyDataConfig.url.match(/__name__=([^\&]+)/) || []
                        this.handleFocus()
                        this.selectSuperSoure = match[1] || ""
                    }
                }
            }
            initAnyway()
            // 获取当前组件API
            // axios.get('/mock/api/dc/DC_QUERY', { params: {id: this.comId} }).then(res=>{
            //     if (res.err !== 0) {
            //         this.$message.error('获取API失败')
            //     } else {
            //         // 有API, 将API配置信息覆盖到原来的配置信息
            //         if(res.data.length!==0){
            //             this.originalSourceId = res.data[0].sourceInfo.id
            //             const content = res.data[0].content
            //             Object.assign(this.copyDataConfig, content)
            //         }
            //     }
            //     initAnyway()
            // })
        },

        // 切换数据源类型
        changeSourceType(source) {
            // 数据源类型改变，仅显示当前数据源类型的数据源
            this.sourceTemplatesByType = this.sourceTemplates.filter(t=>{return t.sourceType===source})
            if (this.sourceTemplatesByType.length!==0) {
                // 没有API的默认选择第一条为最新数据源
                if (!this.originalSourceId) {
                    this.copyDataConfig.templateSourceId =  this.sourceTemplatesByType[0].sourceId
                }
                this.changeTemplateSource(this.copyDataConfig.templateSourceId)
            } else if (this.sourceTemplatesByType.length===0&&source!=='static') {
                this.$message.warn('当前数据源类型缺少数据源，请先创建数据源')
            }

            const { staticData } = this.copyDataConfig
            let data = ['static','file'].indexOf(this.copyDataConfig.source) !== -1 ? staticData : []
            this.staticData = JSON.stringify(data || [], null, 4)
        },

        // 切换数据源
        changeTemplateSource (templateSourceId) {
            if (!templateSourceId) return
            const templateSourceObj = this.sourceTemplatesByType.find((item)=>{
                return item.sourceId === templateSourceId
            })
            this.currSource = templateSourceObj
            if (this.currSource.sourceType === 'file') {
                this.copyDataConfig.staticData = JSON.parse(this.currSource.resData)
            }
            const { staticData } = this.copyDataConfig
            let data = ['static','file'].indexOf(this.copyDataConfig.source) !== -1 ? staticData : []
            this.staticData = JSON.stringify(data || [], null, 4)

            // 初次加载后，清空该变量，因为再改变数据源类型之后，该变量引起冲突
            this.originalSourceId = ''
        },

        // 获取数据源合集
        async getSourceTemplates() {
            const params = {
                key: null,
                sourceType: null,
                current: 1,
                pageSize: 10000
            }
            const res = await axios.get('/mock/api/source/ENUM', { params }) || {}

            if (res.err != 0) {
                this.$message.error('获取数据源模板失败')
            } else {
                this.sourceTemplates = res.data.data
            }
            this.initData()
        },

        // 更新API
        async updateApi (args, isRemove = false) {
            let res
            if (isRemove) {
                res = await axios.get('/mock/api/dc/DC_DEL', args) || {}
            } else {
                res = await axios.post('/mock/api/dc/DC_ADD', args) || {}
            }

			if (res.err != 0) {
				this.$message.error('更新组件数据源信息失败!')
				return
			}

			this.$message.success('更新组件数据源信息成功!')
        },

        // 保存修改 [需要点击'保存修改',才会开始使用指定的数据,或指定的接口]
        saveEdit() {
            const {
                url,
                source,
                type = 'get',
                params = {},
                dataFilterJs,
                dbSql
            } = this.copyDataConfig || {}

            if (source === 'static') {
                try {
                    const staticData = (new Function("return " + this.staticData))() || []
                    this.copyDataConfig.staticData = staticData
                    this.staticData = JSON.stringify(staticData, null, 4)
                } catch (err) {
                    const errObj = new Error(err)
                    this.$confirm({
                        title: "语法错误",
                        icon: createVNode(ExclamationCircleOutlined),
                        content: createVNode({ template: `<div style="color:#B11B22;fontWeight:bold">"${errObj.message}"</div><div style="color:rgba(255, 255, 255, 0.85)">也可查看控制台详情。</div>` }),
                        centered: true,
                        destroyOnClose: true,
                        maskClosable: true,
                        okText: '知道了',
                        cancelText: ''
                    })
                    console.error(err)
                }
            } else if (source === 'database') {
                if (!dbSql) {
                    this.$message.warn('请补充SQL语句')
                }else{
                    const sd = JSON.parse(this.currSource.sourceDetail)
                    const sourceDetail = {
                        host: sd.database.ip,
                        port: sd.database.port,
                        database: sd.database.dbName,
                        user: sd.database.userName,
                        password: sd.database.password,
                    }
                    const strSourceDetail = JSON.stringify(sourceDetail)
                    const base64SourceDetail = window.btoa(strSourceDetail)
                    const params = {
                        sourceType: 'MySQL',
                        sourceDetail: base64SourceDetail,
                        sql: window.btoa(dbSql)
                    }
                    this.copyDataConfig.databaseParams = params
                    axios.post('/mock/api/source/TEST', params).then(res=>{
                        if (res.err !== 0){
                            this.$message.error('执行数据库查询失败')
                            return
                        }
                        this.staticData = JSON.stringify(res.data || [], null, 4)
                    }).catch(err=>{
                        this.$message.error('执行数据库查询失败')
                    })
                }
            } else if (['superSource', 'url'].indexOf(source) !== -1) {
                // 校验查询参数
                try {
                    this.params = this.params.trim()
                    const params = (new Function("return " + this.params))() || {}
                    if (_.isObject(params)) {
                        this.copyDataConfig.params = params
                    } else {
                        throw ("填写的查询参数不是对象形式!")
                    }
                } catch (err) {
                    this.$message.error('填写的查询参数不是对象或存在语法错误!详细见控制台输出.')
                    console.warn('查询参数解析出错:', err)
                    return
                }

                // 校验接口地址
                if (!url) {
                    this.$message.warn('请先填写接口地址!')
                    return
                }

                const root = JSON.parse(this.currSource.sourceDetail).root
                this.copyDataConfig.root = root
                const myUrl = source === 'superSource' ? `${root || window.superSourceRoot}QPROXY${url}` : `${root}/${url}`
                const myParams = type === 'get' ? { params } : params

                axios[type](myUrl, myParams).then(res=>{
                    const resData = T.resDataFilter(res.data || [], dataFilterJs) //数据
                    this.staticData = JSON.stringify(resData || [], null, 4)
                })
            }
            this.myDataConfig = _.cloneDeep(this.copyDataConfig) //触发平台的监听
        },

        filterOption(input, option) {
            //支持label搜索
            let has = option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
            //支持value搜索
            if (!has) has = option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
            return has
        },

        handleFocus() {
            let url = `${window.superSourceRoot}QPROXY?__name__=mgr-vcloud-datas&__result_format__=%s`
            const sd = JSON.parse(this.currSource.sourceDetail)
            if(sd.root){
                if (sd.superSourceProject) {
                    const isNewVersionSupersource = localStorage.getItem('isNewVersionSupersource') || 'true'
                    if (isNewVersionSupersource == 'true') {
                        url = `${sd.root}ENUM_DATAS?interval=proxy&disabled=0&prj=${sd.superSourceProject}`
                    } else {
                        url = `${sd.root}QPROXY?__name__=mgr-vcloud-datas&prj=${sd.superSourceProject}`
                    }
                } else {
                    url = `${sd.root}QPROXY?__name__=mgr-vcloud-datas&__result_format__=%s`
                }
            }
            axios.get(url).then(res=>{
                if (res.data.err!==0) {
                    localStorage.setItem('isNewVersionSupersource', 'false')
                    this.handleFocus()
                    return
                }
                this.superSourceOption = res.data.data.map(o => ({
                    value: o.name,
                    label: o.des
                }))
            })
        },

        handleBlur() {
            // this.superSourceOption = []
        },

        handleChange() {
            if (this.selectSuperSoure===undefined) this.selectSuperSoure = ''
            const sd = JSON.parse(this.currSource.sourceDetail)
            if (sd.superSourceProject) {
                this.copyDataConfig.url = `?__project__=${sd.superSourceProject}&__name__=${this.selectSuperSoure}`
            } else {
                this.copyDataConfig.url = `?__name__=${this.selectSuperSoure}`
            }
        },

        updateConfig() {
            const newConfig = (new Function(`return ${this.dataConfigStr.trim()}`))() || []

            if (newConfig.length === 0) {
                this.$confirm({
                    content: '您确定要清空配置数据吗?',
                    onOk: () => {
                        this.myDataConfig = newConfig
                    },
                    onCancel: () => { }
                })
            } else {
                this.myDataConfig = newConfig
                this.$message.success('修改成功!')
            }
        }
    },
    deactivated() { },
    beforeUnmount() {
        this.$message.destroy()
    },
	unmounted() {
        eventBus.all.delete('editor/index.vue:检查更新API配置信息')
	}
};
</script>

<style scoped lang="less">
.data-config {
    width: 100%;
    height: 100%;
    // width: 16vw;
}
</style>